﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using DiscoveryLogic.Analytics.DataObjects;
using DiscoveryLogic.Analytics.DataObjects.Rules;
using DiscoveryLogic.Workflows.Components.Entities;

namespace DiscoveryLogic.Analytics.BusinessLogic.Dedups
{
    public class EntityDedupWorker
    {
        private EntityDedupRule _DedupRule;

        public EntityDedupWorker(EntityDedupRule rule)
        {
            this._DedupRule = rule;
        }

        public Dictionary<string,List<string>> DedupEntities(List<EntityBase> entities)
        {
            Dictionary<string,List<string>> collapsedMappings=new Dictionary<string, List<string>>();
            Dictionary<string,List<double>> collapsedScore=new Dictionary<string, List<double>>();
            Dictionary<string,EntityBase> processedEntities=new Dictionary<string, EntityBase>();
            for(int i=0;i<entities.Count;i++)
            {
                EntityBase entity1 = entities[i];
                double maxScore = 0;
                string entity2Key = string.Empty;
                foreach(EntityBase entity2 in processedEntities.Values)
                {
                    double assocScore = 0;
                    double dedupScore = 0;
                    this.Compare(entity1, entity2, ref assocScore, ref dedupScore);
                    if(dedupScore>maxScore)
                    {
                        maxScore = dedupScore;
                        entity2Key = entity2.PKValue();
                    }
                }
                if(maxScore>=this._DedupRule.CollapseThreshold)
                {
                    string collapsedKey = string.Empty;
                    foreach(string ck1 in collapsedMappings.Keys)
                    {
                        if(collapsedMappings[ck1].Contains(entity2Key))
                        {
                            collapsedKey = ck1;
                            break;
                        }
                    }
                    List<string> keysCollapsed = collapsedMappings[collapsedKey];
                    keysCollapsed.Add(entity1.PKValue());
                    collapsedMappings[collapsedKey] = keysCollapsed;
                    List<double> scores = collapsedScore[collapsedKey];
                    scores.Add(maxScore);
                    collapsedScore[collapsedKey] = scores;
                }
                else
                {
                    List<string> keysCollapsed=new List<string>();
                    keysCollapsed.Add(entity1.PKValue());
                    collapsedMappings.Add(entity1.PKValue(), keysCollapsed);
                    List<double> scores=new List<double>();
                    collapsedScore.Add(entity1.PKValue(), scores);
                }
                processedEntities.Add(entity1.PKValue(), entity1);
            }

            return collapsedMappings;
        }

        private void Compare(EntityBase entity1, EntityBase entity2, ref double assocScore, ref double dedupScore)
        {
            dedupScore = 0;
            foreach(string descField in this._DedupRule.DescAttrWeights.Keys)
            {
                if(this._DedupRule.Comparers.ContainsKey(descField))
                {
                    PropertyInfo prop = entity1.GetType().GetProperty(descField);
                    object value1 = prop.GetValue(entity1, null);
                    object value2 = prop.GetValue(entity2, null);
                    double penalty = 0;
                    if (this._DedupRule.Penalties.ContainsKey(descField))
                        penalty = this._DedupRule.Penalties[descField];
                    double fieldScore = this._DedupRule.Comparers[descField].Compare(value1, value2, penalty, null,null);
                    dedupScore += fieldScore * this._DedupRule.DescAttrWeights[descField];
                }
            }
            assocScore = dedupScore;
            if (dedupScore >= this._DedupRule.AssocThreshold)
            {
                foreach(string assocField in this._DedupRule.AssocAttrWeights.Keys)
                {
                    PropertyInfo prop = entity1.GetType().GetProperty(assocField);
                    object value1 = prop.GetValue(entity1, null);
                    object value2 = prop.GetValue(entity2, null);
                    double penalty = 0;
                    if (this._DedupRule.Penalties.ContainsKey(assocField))
                        penalty = this._DedupRule.Penalties[assocField];
                    double fieldScore = this._DedupRule.Comparers[assocField].Compare(value1, value2, penalty, null, null);
                    dedupScore += fieldScore * this._DedupRule.AssocAttrWeights[assocField];
                }
            }
        }
    }
}
